﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RunCoachMobileApp.Models;

using System.Collections.ObjectModel;
//using RunCoachMobileApp.RunCoachServiceReference;
//using RunCoachMobileApp.FakeServiceReference;

using System.Windows.Media;
using System.Windows.Threading;
using System.Device.Location;
using Microsoft.Phone.Maps.Controls;
using Windows.Devices.Geolocation;

namespace RunCoachMobileApp.ViewModels
{
    public class ViewModel : ViewModelBase
    {
        #region Private
        private LoginModel _loginModel;
        private RegisterModel _registerModel;
        private FakeServiceClient _client;
        private Int32 _userId;
        /*private String _userName;
        private String _password;*/
        private Boolean _isLoggedIn;
        private Boolean _isActivityStarted;
        private String _buttonContent;
        private String _activityDurationTime;
        private String _actualPace;
        private List<Double> _paces;
        private Double _kilometres;
        private String _distance;
        private String _actualAltitude;

        private String _cartographicMode;
        private Boolean _pedestrianFeaturesEnabled;
        private Boolean _landMarksEnabled;
        private List<String> _cartModes;

        private ObservableCollection<RunIdent> _listOfRuns;

        private GeoCoordinate _center;
        private String _gpsColor;

        private DispatcherTimer _timer;
        private Int64 _startingTime;
        private Int64 _endTime;
        private Int64 _startingPauseTime;
        private Int64 _pauseDurationTime;

        private GeoCoordinateWatcher _watcher;
        private GeoCoordinate _mapCenter;
        private MapPolyline _line;
        private List<GeoCoordinate> _coordList;

        private Int64 _previousPositionChangeTick;

        public event EventHandler LoginSuccess;
        public event EventHandler RegSuccess;
        public event EventHandler RegPage;
        public event EventHandler HistoryPage;
        #endregion

        #region Commands
        public DelegateCommand StarterCommand { get; private set; }
        public DelegateCommand StopCommand { get; private set; }
        public DelegateCommand LoginCommand { get; private set; }
        public DelegateCommand RegisterCommand { get; private set; }
        public DelegateCommand SwitchToRegisterCommand { get; private set; }
        public DelegateCommand HistoryCommand { get; private set; }
        #endregion

        #region Properties
        public Int32 UserId { get { return _userId; } set { _userId = value; } }
        public String UserName { get { return _loginModel.UserName; } set { _loginModel.UserName = value; } }
        public String Password { get { return _loginModel.Password; } set { _loginModel.Password = value; } }

        public String RegUserName { get { return _registerModel.UserName; } set { _registerModel.UserName = value; } }
        public String RegFullName { get { return _registerModel.FullName; } set { _registerModel.FullName = value; } }
        public String RegPassword { get { return _registerModel.Password; } set { _registerModel.Password = value; } }

        public String GPSColor { get { return _gpsColor; } set { _gpsColor = value; OnPropertyChanged("GPSColor"); } }
        public String ButtonContent { get { return _buttonContent; } set { _buttonContent = value; OnPropertyChanged("ButtonContent"); } }

        public String Time { get { return _activityDurationTime; } set { _activityDurationTime = value; OnPropertyChanged("Time"); } }
        public String Pace { get { return _actualPace; } set { _actualPace = value; OnPropertyChanged("Pace"); } }
        public String Distance { get { return _distance; } set { _distance = value; OnPropertyChanged("Distance"); } }
        public String Altitude { get { return _actualAltitude; } set { _actualAltitude = value; OnPropertyChanged("Altitude"); } }

        public GeoCoordinate Center { get { return _center; } set { _center = value; OnPropertyChanged("Center"); } }
        public GeoCoordinate MapCenter { get { return _mapCenter; } set { _mapCenter = value; OnPropertyChanged("MapCenter"); } }
        public MapPolyline Line { get { return _line; } }
        public String CartographicMode { get { return _cartographicMode; } set { _cartographicMode = value; OnPropertyChanged("CartographicMode"); } }
        public Boolean PedestrianFeaturesEnabled { get { return _pedestrianFeaturesEnabled; } set { _pedestrianFeaturesEnabled = value; OnPropertyChanged("PedestrianFeaturesEnabled"); } }
        public Boolean LandMarksEnabled { get { return _landMarksEnabled; } set { _landMarksEnabled = value; OnPropertyChanged("LandMarksEnabled"); } }

        public Boolean IsLoggedIn { get { return _isLoggedIn; } set { _isLoggedIn = value; } }

        public ObservableCollection<RunIdent> ListOfRuns { get { return _listOfRuns; } set { _listOfRuns = value; OnPropertyChanged("ListOfRuns"); } }

        //public Int32 RunId { get { return } }
        #endregion

        #region Constructor
        public ViewModel()
        {
            _client = new FakeServiceClient();
            InitCommands();
            InitEventHandlers();
            InitTimer();
            InitWatcher();
            InitMap();
            InitContent();

            GPSColor = "Red";

            _isActivityStarted = false;
            _loginModel = new LoginModel();
            _registerModel = new RegisterModel();
            _isLoggedIn = false;
        }
        #endregion

        #region Initialization
        private void InitCommands()
        {
            StarterCommand = new DelegateCommand(param => OnStartActivity());
            StopCommand = new DelegateCommand(param => OnStopActivity());
            LoginCommand = new DelegateCommand(param => OnLoginExecuted());
            RegisterCommand = new DelegateCommand(param => OnRegisterExecuted());
            SwitchToRegisterCommand = new DelegateCommand(param => SwitchToRegisterExecuted());
            HistoryCommand = new DelegateCommand(param => SwitchToHistoryExecuted());
        }

        private void InitEventHandlers() {}

        private void InitTimer() 
        {
            _timer = new DispatcherTimer();
            _timer.Interval = TimeSpan.FromSeconds(1);
            _timer.Tick += Timer_Tick;
        }

        private void InitWatcher()
        {
            _watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
            _watcher.PositionChanged += Watcher_PositionChanged;
            _watcher.Start();
        }

        private void InitMap()
        {
            _mapCenter = new GeoCoordinate();
            _cartModes = new List<String> { "Terrain", "Road", "Aerial", "Hybrid" };
            CartographicMode = _cartModes[0];
            PedestrianFeaturesEnabled = true;
            LandMarksEnabled = true;
        }

        private void InitContent()
        {
            _line = new MapPolyline(); 
            _line.StrokeColor = Colors.Red;
            _line.StrokeThickness = 5;
            _coordList = new List<GeoCoordinate>();
            _kilometres = 0;
            _paces = new List<Double>();
            _listOfRuns = new ObservableCollection<RunIdent>();

            ButtonContent = "Start";

            Time = TimeSpan.FromMilliseconds(0).ToString(@"hh\:mm\:ss");
            Pace = TimeSpan.FromMilliseconds(0).ToString(@"mm\:ss") + " min/km";
            Altitude = String.Format("{0:f2} m", 0);
            Distance = String.Format("{0:f2} km", 0);
        }
        #endregion

        private void OnLoginExecuted()
        {
            if ((UserId = _client.Login(UserName, Password)) != 0)
            {
                if (LoginSuccess != null)
                {
                    LoginSuccess(this, EventArgs.Empty);
                }
            }            
        }


        private void OnRegisterExecuted()
        {
            if (_client.RegisterUser(RegUserName, RegFullName, RegPassword))
            {
                UserName = RegUserName;
                Password = RegPassword;
                if(RegSuccess != null)
                {
                    RegSuccess(this, EventArgs.Empty);
                }
            }

        }

        private void SwitchToRegisterExecuted()
        {
            if (RegPage != null)
            {
                RegPage(this, EventArgs.Empty);
            }
        }

        private void SwitchToHistoryExecuted()
        {
            var AllRuns = _client.getAllRunsData(UserId);
            foreach (var run in AllRuns)
            {
                ListOfRuns.Add(new RunIdent(run.id, run.start_time.ToString()));
            }
            if (HistoryPage != null)
            {
                HistoryPage(this, EventArgs.Empty);
            }
        }

        private void OnStopActivity()
        {
            ButtonContent = "Start";
            _endTime = System.Environment.TickCount;
            _timer.Stop();
            _isActivityStarted = false;
            Dictionary<String, Double> statistics = new Dictionary<string, Double>{
                {"avg_speed1", 1/(60*_paces.Sum()/_paces.Count())},
                {"avg_speed2", _paces.Sum()/_paces.Count()},
                {"distance", _kilometres},
                {"user_id", UserId}
            };
            SaveActivity(statistics);
        }

        private void OnStartActivity()
        {
            if (_isActivityStarted)
            {
                PauseOrContinueActivity();
            }
            else
            {
                StartNewActivity();
            }
        }

        private void PauseOrContinueActivity()
        {
            if (_timer.IsEnabled)
            {
                PauseActivity();
            }
            else
            {
                ContinueActivity();
            }
        }

        private void PauseActivity()
        {
            _timer.Stop();
            _startingPauseTime = System.Environment.TickCount;
            ButtonContent = "Start";
        }

        private void ContinueActivity()
        {
            _timer.Start();
            _pauseDurationTime += System.Environment.TickCount - _startingPauseTime;
            ButtonContent = "Pause";
        }

        private void StartNewActivity()
        {            
            _isActivityStarted = true;
            _timer.Start();
            _startingTime = System.Environment.TickCount;
            _pauseDurationTime = 0;

            InitContent();
            ButtonContent = "Pause";
        }

        private void SaveActivity(Dictionary<String, Double> statistics)
        {
            if(!_client.getDataFromMobile(new DateTime(_startingTime), new DateTime(_endTime), _coordList, statistics))
            {

            }
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            Time = TimeSpan.FromMilliseconds(System.Environment.TickCount - _startingTime - _pauseDurationTime).ToString(@"hh\:mm\:ss");
        }

        private void Watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
        {
            Center = new GeoCoordinate(e.Position.Location.Latitude, e.Position.Location.Longitude, e.Position.Location.Altitude);
            MapCenter = new GeoCoordinate(e.Position.Location.Latitude, e.Position.Location.Longitude);
            GPSColor = "Green";

            ProcessPositionChange();
        }

        private void ProcessPositionChange()
        {
            if (IsActivityOn())
            {
                ActualizeContent();
            }
        }

        private Boolean IsActivityOn()
        {
            return _isActivityStarted && _timer.IsEnabled;
        }

        private void ActualizeContent()
        {
            if (_line.Path.Count > 0)
            {
                ComputeDisplayingData();
            }

            _line.Path.Add(_center);
            _coordList.Add(_center);
            _previousPositionChangeTick = System.Environment.TickCount;
        }

        private void ComputeDisplayingData()
        {
            var previousPoint = _line.Path.Last();
            var partialDistance = Center.GetDistanceTo(previousPoint);
            var actualAltitude = Center.Altitude;

            var deltaTimeInMillis = System.Environment.TickCount - _previousPositionChangeTick;
            var actualPaceInMilliSec = (1000.0 / partialDistance) * deltaTimeInMillis;

            _kilometres += partialDistance / 1000.0;

            _paces.Add(actualPaceInMilliSec);
            Pace = TimeSpan.FromMilliseconds(actualPaceInMilliSec).ToString(@"mm\:ss") + " min/km";
            Distance = String.Format("{0:f2} km", _kilometres);
            Altitude = String.Format("{0:f2} m", actualAltitude);
        }
        
    }
}
